home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / WINDOWS / WINPOEM.ARJ / WINPOEM.CPP < prev    next >
C/C++ Source or Header  |  1992-04-03  |  43KB  |  1,546 lines

  1. /**************************************************************************
  2.  * WinPoem                                                                *
  3.  *                                                                        *
  4.  * A small C++ program which displays a random poem on execution.         *
  5.  * It also allows search for poems containing a string.                   *
  6.  * It requires winpoem.dat and creates winpoem.idx.                       *
  7.  *                                                                        *
  8.  * Copyright 1992 Julian Smart, email jacs@aiai.ed.ac.uk                  *
  9.  *                                                                        *
  10.  * Filename: poetry.cpp                                                   *
  11.  * Purpose:  Main source file for WinPoem.                                *
  12.  * Version:  1.0                                                          *
  13.  *************************************************************************/
  14.  
  15. #include <windows.h>
  16.  
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <time.h>
  21.  
  22. #define         buf_size 3000
  23. #define         DEFAULT_POETRY_DAT "winpoem"
  24. #define         DEFAULT_POETRY_IND "winpoem"
  25. #define         DEFAULT_CHAR_HEIGHT 18
  26. #define         DEFAULT_FONT "Swiss"
  27. #define         DEFAULT_X_POS 0
  28. #define         DEFAULT_Y_POS 0
  29. #define         BORDER_SIZE 30
  30. #define         THIN_LINE_BORDER 10
  31. #define         THICK_LINE_BORDER 16
  32. #define         THICK_LINE_WIDTH 2
  33. #define         SHADOW_OFFSET 1
  34. #define         X_SIZE 30
  35. #define         Y_SIZE 20
  36.  
  37. static char     poem_buffer[buf_size];          // Storage for each poem
  38. static char     line[120];                      // Storage for a line
  39. static char     title[120];                     // Remember the title
  40. static char     search_string[120];             // The search string
  41. static int      pages[20];                      // For multipage poems -
  42.                                                 // store the start of each page
  43. static long     last_poem_start = 0;            // Start of last found poem
  44. static long     last_find = -1;                 // Point in file of last found
  45.                                                 // search string
  46. static BOOL     search_ok = FALSE;              // Search was successful
  47. static BOOL     same_search = FALSE;            // Searching on same string
  48.  
  49. static long     index[600];                     // Index of poem starts
  50. static long     nitems = 0;                     // Number of poems
  51. static int      desired_char_height = DEFAULT_CHAR_HEIGHT; // Desired height
  52. static char     DesiredFont[64];                // Chosen font
  53. static int      char_height = DEFAULT_CHAR_HEIGHT; // Actual height
  54. static int      index_ptr = -1;                 // Pointer into index
  55. static int      poem_height, poem_width;        // Size of poem window
  56. static HWND     GlobalWindow;                   // Window handle
  57. static HANDLE   bitmap = NULL;                  // Window backing bitmap
  58. static HMENU    PopupMenu;                      // WinPoem menu
  59. static int      XPos;                           // Startup X position
  60. static int      YPos;                           // Startup Y position
  61.  
  62. static char     index_filename[100];            // Index filename
  63. static char     data_filename[100];             // Data filename
  64. static char     error_buf[200];                 // Error message buffer
  65. static BOOL     loaded_ok = FALSE;              // Poem loaded ok
  66. static BOOL     index_ok = FALSE;               // Index loaded ok
  67.  
  68. static BOOL     paging = FALSE;                 // Are we paging?
  69. static int      current_page = 0;               // Currently viewed page
  70.  
  71. static HICON    Corner1;                        // Shell corner icons
  72. static HICON    Corner2;
  73. static HICON    Corner3;
  74. static HICON    Corner4;
  75.  
  76. static HPEN     WhitePen;                       // Pens
  77. static HPEN     BlackPen;
  78. static HPEN     GreyPen;
  79. static HBRUSH   GreyBrush;
  80. static HBRUSH   DarkGreyBrush;
  81.  
  82. // Fonts
  83. static HFONT    NormalFont = NULL;              // Fonts
  84. static HFONT    BoldFont = NULL;
  85. static HFONT    ItalicFont = NULL;
  86.  
  87. void            PoetryError(char *);
  88. void            PoetryNotify(char *Msg);
  89. void            InitPoetry();
  90. void            TryLoadIndex();
  91. BOOL            LoadPoem(char *, long);
  92. int             GetIndex();
  93. int             LoadIndex(char *);
  94. void            LogMessage(char *Msg);
  95. void            LogInitialize();
  96. BOOL            Compile();
  97. void            WritePreferences();
  98. void            ReadPreferences();
  99. void            DeleteFonts(void);
  100. void            FindMax(int *max_thing, int thing);
  101. void            CreateFonts();
  102. void            CopyToClipboard(HWND, char *);
  103.  
  104. BOOL FAR PASCAL _export AboutDlgProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam);
  105. long FAR PASCAL _export WndProc( HWND hWnd, WORD iMessage,
  106.                                  WORD wParam, LONG lParam );
  107. BOOL FAR PASCAL _export SearchDlgProc(HWND hDlg, unsigned message,
  108.                                       WORD wParam, LONG lParam);
  109. class Main
  110. {
  111.     public:
  112.     static HANDLE hInstance;
  113.     static HANDLE hPrevInstance;
  114.     static int nCmdShow;
  115.     static int MessageLoop( void );
  116. };
  117.  
  118. HANDLE Main::hInstance = 0;
  119. HANDLE Main::hPrevInstance = 0;
  120. int Main::nCmdShow = 0;
  121.  
  122. // Message loop
  123. int Main::MessageLoop( void )
  124. {
  125.     MSG msg;
  126.  
  127.     while( GetMessage( &msg, NULL, 0, 0 ) )
  128.     {
  129.         TranslateMessage( &msg );
  130.         DispatchMessage( &msg );
  131.     }
  132.     return msg.wParam;
  133. }
  134.  
  135. // Base class
  136. class Window
  137. {
  138.     protected:
  139.         HWND hWnd;
  140.     public:
  141.         // Provide (read) access to the window's handle in case it is needed
  142.         // elsewhere.
  143.         HWND GetHandle( void ) { return hWnd; }
  144.  
  145.         BOOL Show( int nCmdShow ) { return ShowWindow( hWnd, nCmdShow ); }
  146.         void Update( void ) { UpdateWindow( hWnd ); }
  147.         // Pure virtual function makes Window an abstract class.
  148.         virtual long WndProc( WORD iMessage, WORD wParam, LONG lParam ) = 0;
  149. };
  150.  
  151. class MainWindow : public Window
  152. {
  153.     private:
  154.         static char szClassName[14];
  155.     public:
  156.         // Register the class only AFTER WinMain assigns appropriate
  157.         // values to static members of Main and only if no previous
  158.         // instances of the program exist (a previous instance would
  159.         // have already performed the registration).
  160.         static void Register( void )
  161.         {
  162.             WNDCLASS wndclass;   // Structure used to register Windows class.
  163.  
  164.             wndclass.style         = CS_HREDRAW | CS_VREDRAW;
  165.             wndclass.lpfnWndProc   = ::WndProc;
  166.             wndclass.cbClsExtra    = 0;
  167.             // Reserve extra bytes for each instance of the window;
  168.             // we will use these bytes to store a pointer to the C++
  169.             // (MainWindow) object corresponding to the window.
  170.             // the size of a 'this' pointer depends on the memory model.
  171.             wndclass.cbWndExtra    = sizeof( MainWindow * );
  172.             wndclass.hInstance     = Main::hInstance;
  173.             wndclass.hIcon         = LoadIcon( Main::hInstance, "winpoem" );
  174.             wndclass.hCursor       = LoadCursor( NULL, IDC_ARROW );
  175.             wndclass.hbrBackground = GetStockObject( LTGRAY_BRUSH );
  176.             wndclass.lpszMenuName  = NULL;
  177.             wndclass.lpszClassName = szClassName;
  178.  
  179.             if ( ! RegisterClass( &wndclass ) )
  180.                 exit( FALSE );
  181.         }
  182.  
  183.  
  184.         // Do not create unless previously registered.
  185.         MainWindow( void )
  186.         {
  187.             // Pass 'this' pointer in lpParam of CreateWindow().
  188.             hWnd = CreateWindow( szClassName,
  189.                 szClassName,
  190.                 WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE,
  191.                 XPos, YPos,
  192.                 X_SIZE, SW_HIDE,
  193.                 NULL, NULL, Main::hInstance,
  194.                 (LPSTR) this );
  195.  
  196.             GlobalWindow = hWnd;
  197.  
  198.             if ( ! hWnd )
  199.                 exit( FALSE );
  200.  
  201.             Show( Main::nCmdShow );
  202.         }
  203.         long WndProc( WORD iMessage, WORD wParam, LONG lParam );
  204.  
  205.         // Print a message in the client rectangle.
  206.         void Paint( void );
  207.  
  208.         // Display next page or poem
  209.         void NextPage(HWND);
  210.  
  211.         // Display previous page
  212.         void PreviousPage(HWND);
  213.  
  214.         // User search
  215.         void Search(HWND, BOOL);
  216.  
  217.         // Look in file for string
  218.         long DoSearch();
  219.  
  220.         // Do the actual drawing of text (or just calculate size needed)
  221.         void ScanBuffer(BOOL DrawIt, int *max_x, int *max_y);
  222.  
  223.         // Load the poem
  224.         void GetIndexLoadPoem(void);
  225.         void Resize();
  226.  
  227.         // Respond to a command menu message
  228.         BOOL Command(WORD);
  229.  
  230.         // Respond to a keypress
  231.         void KeyDown(WORD);
  232.         void Char(WORD);
  233.  
  234.         // Change text height (refresh screen)
  235.         void ChangeHeight(int);
  236.  
  237. };
  238.  
  239. char MainWindow::szClassName[] = "WinPoem";
  240.  
  241. // Paint procedure
  242. void MainWindow::Paint( void )
  243. {
  244.     int max_x, max_y;
  245.     PAINTSTRUCT ps;
  246.     RECT rect;
  247.     HDC hdcMem = NULL;
  248.     HDC hdc;
  249.  
  250.     // If we have a bitmap with a poem drawn on it,
  251.     // whack it onto the window.
  252.     if (bitmap)
  253.     {
  254.       GetClientRect( hWnd, (LPRECT) &rect );
  255.       hdc = BeginPaint( hWnd, &ps );
  256.       hdcMem = CreateCompatibleDC(hdc);
  257.       SelectObject(hdcMem, bitmap);
  258.       BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);
  259.       DeleteDC(hdcMem);
  260.       EndPaint(hWnd, &ps);
  261.     }
  262.     else
  263.     {
  264.       // Necessary default behaviour
  265.       (void)BeginPaint( hWnd, &ps );
  266.       EndPaint(hWnd, &ps);
  267.     }
  268. }
  269.  
  270. // Change the text height and redraw
  271. void MainWindow::ChangeHeight(int inc)
  272. {
  273.     PAINTSTRUCT ps;
  274.     TEXTMETRIC lpTextMetric;
  275.     HDC hdc = BeginPaint( hWnd, &ps );
  276.  
  277.     SelectObject(hdc, NormalFont);
  278.     GetTextMetrics(hdc, &lpTextMetric);
  279.  
  280.     int old_height = lpTextMetric.tmHeight;
  281.     int actual_height = old_height;
  282.  
  283.     // Keep changing font size until it really changes height
  284.     while (actual_height == old_height && desired_char_height > 1
  285.                                        && desired_char_height < 40)
  286.     {
  287.       desired_char_height += inc;
  288.       CreateFonts();
  289.  
  290.       // See what ACTUAL char height is
  291.       SelectObject(hdc, NormalFont);
  292.       GetTextMetrics(hdc, &lpTextMetric);
  293.       actual_height = lpTextMetric.tmHeight;
  294.     }
  295.     // Stop ChangeHeight from getting stuck next time
  296.     if (desired_char_height == 1)
  297.       desired_char_height = 2;
  298.     if (desired_char_height == 40)
  299.       desired_char_height = 39;
  300.     EndPaint(hWnd, &ps);
  301.     Resize();
  302.     InvalidateRect(hWnd, NULL, TRUE);
  303.     UpdateWindow(hWnd);
  304. }
  305.  
  306. // Create the fonts
  307. void CreateFonts()
  308. {
  309.   // Create fonts
  310.   BYTE Font;
  311.   char *TypeFace = "Any";
  312.  
  313.   if (strcmp(DesiredFont, "Modern") == 0)
  314.     Font = FF_MODERN;
  315.   else if (strcmp(DesiredFont, "Script") == 0)
  316.     Font = FF_SCRIPT;
  317.   else if (strcmp(DesiredFont, "Swiss") == 0)
  318.     Font = FF_SWISS;
  319.   else if (strcmp(DesiredFont, "Roman") == 0)
  320.     Font = FF_ROMAN;
  321.   else if (strcmp(DesiredFont, "Decorative") == 0)
  322.     Font = FF_DECORATIVE;
  323.   else
  324.     Font = FF_DONTCARE;
  325.  
  326.   if (!NormalFont)
  327.     DeleteObject(NormalFont);
  328.   NormalFont = CreateFont(desired_char_height, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
  329.                        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
  330.                        PROOF_QUALITY, VARIABLE_PITCH | Font,
  331.                        TypeFace);
  332.   if (!ItalicFont)
  333.     DeleteObject(ItalicFont);
  334.   ItalicFont = CreateFont(desired_char_height, 0, 0, 0, FW_BOLD, 1, 0, 0, ANSI_CHARSET,
  335.                        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
  336.                        PROOF_QUALITY, VARIABLE_PITCH | Font,
  337.                        TypeFace);
  338.   if (!BoldFont)
  339.     DeleteObject(BoldFont);
  340.   BoldFont = CreateFont(desired_char_height, 0, 0, 0, FW_BOLD, 0, 0, 0, ANSI_CHARSET,
  341.                        OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
  342.                        PROOF_QUALITY, VARIABLE_PITCH | Font,
  343.                        TypeFace);
  344. }
  345.  
  346. // Delete fonts
  347. void DeleteFonts(void)
  348. {
  349.   DeleteObject(NormalFont);
  350.   DeleteObject(BoldFont);
  351.   DeleteObject(ItalicFont);
  352. }
  353.  
  354. // Read the poetry buffer, either for finding the size
  355. // or for writing to a bitmap (not to the window directly,
  356. // since that displays messily)
  357. // If DrawIt is true, we draw, otherwise we just determine the
  358. // size the window should be.
  359. void MainWindow::ScanBuffer(BOOL DrawIt, int *max_x, int *max_y)
  360. {
  361.     PAINTSTRUCT ps;
  362.     RECT rect;
  363.     TEXTMETRIC lpTextMetric;
  364.     HDC hdc = BeginPaint( hWnd, &ps );
  365.     HDC hdcMem = NULL;
  366.  
  367.     int i = pages[current_page];
  368.     int ch = -1;
  369.     short x = 10;
  370.     short y = 0;
  371.     int j;
  372.     char *line_ptr;
  373.     int strsize;
  374.     int loword;
  375.     int x1;
  376.     DWORD extent;
  377.     int curr_width = 0;
  378.     BOOL page_break = FALSE;
  379.  
  380.     short width = 0;
  381.     short height = 0;
  382.  
  383.     SetBkMode(hdc, TRANSPARENT);
  384.  
  385.     if (DrawIt)
  386.     {
  387.       width = *max_x;
  388.       height = *max_y;
  389.       rect.left = 0; rect.top = 0;
  390.       rect.right = width; rect.bottom = height;
  391.  
  392.       y = (height - (poem_height))/2;
  393.  
  394.       if (bitmap)
  395.       {
  396.         DeleteObject(bitmap);
  397.         bitmap = NULL;
  398.       }
  399.  
  400.       hdcMem = CreateCompatibleDC(hdc);
  401.       if (hdcMem)
  402.       {
  403.         bitmap = CreateCompatibleBitmap(hdc, width, height);
  404.         if (bitmap)
  405.         {
  406.           SelectObject(hdcMem, bitmap);
  407.           SetBkMode(hdcMem, TRANSPARENT);
  408.           FillRect( hdcMem, &rect, GetStockObject( LTGRAY_BRUSH ) );
  409.         }
  410.         else
  411.         {
  412.           DeleteDC(hdcMem);
  413.           hdcMem = hdc;
  414.         }
  415.       }
  416.     }
  417.  
  418.     // See what ACTUAL char height is
  419.     SelectObject(hdc, NormalFont);
  420.     GetTextMetrics(hdc, &lpTextMetric);
  421.     char_height = lpTextMetric.tmHeight;
  422.  
  423.     if (current_page == 0)
  424.       title[0] = 0;
  425.     else if (title[0] != 0)
  426.     {
  427.        SelectObject(hdc, BoldFont);
  428.        strsize = strlen(title);
  429.        extent = GetTextExtent(hdc, title, strsize);
  430.        loword = LOWORD(extent);
  431.        FindMax(&curr_width, loword);
  432.  
  433.        if (DrawIt)
  434.        {
  435.          x1 = width - loword;
  436.          x = x1/2;
  437.          SelectObject(hdcMem, BoldFont);
  438.  
  439.          // Change text to WHITE!
  440.          SetTextColor(hdcMem, RGB(0, 0, 0));
  441.          TextOut(hdcMem, x, y, title, strsize);
  442.          // Change text to BLACK!
  443.          SetTextColor(hdcMem, RGB(255, 255, 255));
  444.          TextOut(hdcMem, x-SHADOW_OFFSET, y-SHADOW_OFFSET, title, strsize);
  445.          SetTextColor(hdcMem, RGB(0, 0, 0));
  446.        }
  447.        y += char_height;
  448.        y += char_height;
  449.     }
  450.  
  451.     while (ch != 0 && !page_break)
  452.     {
  453.         j = 0;
  454.         while (((ch = poem_buffer[i]) != 13) && (ch != 0))
  455.         {
  456.             line[j] = ch;
  457.             j ++;
  458.             i ++;
  459.         }
  460.  
  461.         if (ch == 13)
  462.         {
  463.             ch = -1;
  464.             i ++;
  465.             // Add another to skip the linefeed
  466.             i ++;
  467.             // If a single newline on its own, put a space in
  468.             if (j == 0)
  469.             {
  470.               line[j] = ' ';
  471.               j ++;
  472.               line[j] = 0;
  473.             }
  474.         }
  475.  
  476.         if (j > 0)
  477.         {
  478.           line[j] = 0;
  479.           if (line[0] == '@')
  480.           {
  481.             switch (line[1])
  482.             {
  483.               case 'P':
  484.                 paging = TRUE;
  485.                 page_break = TRUE;
  486.                 break;
  487.  
  488.               case 'T':
  489.                 SelectObject(hdc, BoldFont);
  490.                 line_ptr = line+3;
  491.  
  492.                 strsize = strlen(line_ptr);
  493.                 strcpy(title, line_ptr);
  494.                 strcat(title, " (cont'd)");
  495.  
  496.                 extent = GetTextExtent(hdc, line_ptr, strsize);
  497.                 loword = LOWORD(extent);
  498.                 FindMax(&curr_width, loword);
  499.  
  500.                 if (DrawIt)
  501.                 {
  502.                   x1 = width - loword;
  503.                   x = x1/2;
  504.                   SelectObject(hdcMem, BoldFont);
  505.  
  506.                   // Change text to WHITE!
  507.                   SetTextColor(hdc, RGB(0, 0, 0));
  508.                   TextOut(hdcMem, x, y, line_ptr, strsize);
  509.                   // Change text to BLACK!
  510.                   SetTextColor(hdcMem, RGB(255, 255, 255));
  511.                   TextOut(hdcMem, x-SHADOW_OFFSET, y-SHADOW_OFFSET, line_ptr, strsize);
  512.                   SetTextColor(hdcMem, RGB(0, 0, 0));
  513.                 }
  514.                 break;
  515.  
  516.               case 'A':
  517.                 line_ptr = line+3;
  518.                 SelectObject(hdc, ItalicFont);
  519.                 strsize = strlen(line_ptr);
  520.  
  521.                 extent = GetTextExtent(hdc, line_ptr, strsize);
  522.                 loword = LOWORD(extent);
  523.                 FindMax(&curr_width, loword);
  524.  
  525.                 if (DrawIt)
  526.                 {
  527.                   x1 = width - loword;
  528.                   x = x1/2;
  529.                   SelectObject(hdcMem, ItalicFont);
  530.                   SetTextColor(hdcMem, RGB(0, 0, 0));
  531.                   TextOut(hdcMem, x, y, line_ptr, strsize);
  532.                 }
  533.                 break;
  534.  
  535.               // Default: just ignore this line
  536.               default:
  537.                 y -= char_height;
  538.             }
  539.            }
  540.            else
  541.            {
  542.               SelectObject(hdc, NormalFont);
  543.               strsize = strlen(line);
  544.  
  545.               extent = GetTextExtent(hdc, line, strsize);
  546.               int loword = LOWORD(extent);
  547.               FindMax(&curr_width, loword);
  548.     
  549.               if (DrawIt)
  550.               {
  551.                 x1 = width  - loword;
  552.                 int x = x1/2;
  553.                 SelectObject(hdcMem, NormalFont);
  554.                 SetTextColor(hdcMem, RGB(0, 0, 0));
  555.                 TextOut(hdcMem, x, y, line, strsize);
  556.               }
  557.            }
  558.         }
  559.         y += char_height;
  560.     }
  561.  
  562.     // Write (cont'd)
  563.     if (page_break)
  564.     {
  565.        char *cont = "(cont'd)";
  566.  
  567.        SelectObject(hdc, NormalFont);
  568.        strsize = strlen(cont);
  569.  
  570.        extent = GetTextExtent(hdc, cont, strsize);
  571.        int loword = LOWORD(extent);
  572.        FindMax(&curr_width, loword);
  573.  
  574.        if (DrawIt)
  575.        {
  576.          x1 = width  - loword;
  577.          int x = x1/2;
  578.          SelectObject(hdcMem, NormalFont);
  579.          SetTextColor(hdcMem, RGB(255, 255, 255));
  580.          TextOut(hdcMem, x, y, cont, strsize);
  581.        }
  582.        y += 2*char_height;
  583.     }
  584.  
  585.     *max_x = curr_width;
  586.     *max_y = y-char_height;
  587.  
  588.     if (page_break)
  589.       pages[current_page+1] = i;
  590.     else
  591.       paging = FALSE;
  592.  
  593.     if (DrawIt)
  594.     {
  595.     // Draw dark grey thick border
  596.     SelectObject(hdcMem, DarkGreyBrush);
  597.     SelectObject(hdcMem, GreyPen);
  598.  
  599.     // Left side
  600.     Rectangle(hdcMem, 0, THIN_LINE_BORDER, THIN_LINE_BORDER, height-THIN_LINE_BORDER);
  601.     // Top side
  602.     Rectangle(hdcMem, THIN_LINE_BORDER, 0, width-THIN_LINE_BORDER, THIN_LINE_BORDER);
  603.     // Right side
  604.     Rectangle(hdcMem, width-THIN_LINE_BORDER, THIN_LINE_BORDER, width, height-THIN_LINE_BORDER);
  605.     // Bottom side
  606.     Rectangle(hdcMem, THIN_LINE_BORDER, height-THIN_LINE_BORDER, width-THIN_LINE_BORDER, height);
  607.  
  608.     // Draw border
  609.     // Have grey background, plus 3-d border -
  610.     // One black rectangle.
  611.     // Inside this, left and top sides - dark grey. Bottom and right -
  612.     // white.
  613.  
  614.     // Change pen to black
  615.     SelectObject(hdcMem, BlackPen);
  616.     MoveTo(hdcMem, THIN_LINE_BORDER, THIN_LINE_BORDER);
  617.     LineTo(hdcMem, width-THIN_LINE_BORDER, THIN_LINE_BORDER);
  618.     LineTo(hdcMem, width-THIN_LINE_BORDER, height-THIN_LINE_BORDER);
  619.     LineTo(hdcMem, THIN_LINE_BORDER, height-THIN_LINE_BORDER);
  620.     LineTo(hdcMem, THIN_LINE_BORDER, THIN_LINE_BORDER);
  621.  
  622.     // Right and bottom white lines
  623.     SelectObject(hdcMem, WhitePen);
  624.     MoveTo(hdcMem, width-THICK_LINE_BORDER,
  625.                 THICK_LINE_BORDER);
  626.     LineTo(hdcMem, width-THICK_LINE_BORDER,
  627.                 height-THICK_LINE_BORDER);
  628.     LineTo(hdcMem, THICK_LINE_BORDER,
  629.                 height-THICK_LINE_BORDER);
  630.  
  631.     // Left and top grey lines
  632.     SelectObject(hdcMem, GreyPen);
  633.     LineTo(hdcMem, THICK_LINE_BORDER, THICK_LINE_BORDER);
  634.     LineTo(hdcMem, width-THICK_LINE_BORDER, THICK_LINE_BORDER);
  635.  
  636.     // Draw icons
  637.     SelectObject(hdcMem, BlackPen);
  638.     DrawIcon(hdcMem, 0, 0, Corner1);
  639.     DrawIcon(hdcMem, width-32, 0, Corner2);
  640.  
  641.     int y2 = height - 32;
  642.     int x2 = (width-32);
  643.     DrawIcon(hdcMem, 0, y2, Corner3);
  644.     DrawIcon(hdcMem, x2, y2, Corner4);
  645.  
  646.     DeleteDC(hdcMem);
  647.  
  648.     }
  649.  
  650.     EndPaint(hWnd, &ps);
  651. }
  652.  
  653. // Get an index (randomly generated) and load the poem
  654. void MainWindow::GetIndexLoadPoem(void)
  655. {
  656.     if (index_ok)
  657.       index_ptr = GetIndex();
  658.  
  659.     if (index_ptr > -1)
  660.       loaded_ok = LoadPoem(data_filename, -1);
  661. }
  662.  
  663. // Find the size of the poem and resize the window accordingly
  664. void MainWindow::Resize()
  665. {
  666.     int max_x, max_x1;
  667.     int max_y, max_y1;
  668.     RECT Rect;
  669.     RECT ClientRect;
  670.     GetWindowRect(GetHandle(), &Rect);
  671.     GetClientRect( hWnd, (LPRECT) &ClientRect );
  672.  
  673.     // Calculate what to add on for the window borders & menu bar
  674.     int DiffX = (Rect.right - Rect.left) - ClientRect.right;
  675.     int DiffY = (Rect.bottom - Rect.top) - ClientRect.bottom;
  676.  
  677.     // Get the poem size
  678.     ScanBuffer(FALSE, &poem_width, &poem_height);
  679.     max_x = poem_width + (2*BORDER_SIZE);
  680.     max_y = poem_height + (2*BORDER_SIZE);
  681.     max_x1 = max_x;
  682.     max_y1 = max_y;
  683.  
  684.     // Actually draw it
  685.     ScanBuffer(TRUE, &max_x1, &max_y1);
  686.  
  687.     MoveWindow(GetHandle(), Rect.left, Rect.top, max_x+DiffX, max_y+DiffY, TRUE);
  688.     SetFocus(GetHandle());
  689. }
  690.  
  691. // Which is more?
  692. void FindMax(int *max_thing, int thing)
  693. {
  694.   if (thing > *max_thing)
  695.     *max_thing = thing;
  696. }
  697.  
  698. // Process menu commands
  699. // Return TRUE if processed one
  700. BOOL MainWindow::Command(WORD Item)
  701. {
  702.   HWND handle = GetHandle();
  703.   FARPROC lpProcAbout;
  704.  
  705.   switch (Item)
  706.   {
  707.      case 101:
  708.        // Another poem/page
  709.        NextPage(handle);
  710.        break;
  711.      case 113:
  712.        // Previous page
  713.        PreviousPage(handle);
  714.        break;
  715.      case 114:
  716.        // Search - with dialog
  717.        Search(handle, TRUE);
  718.        break;
  719.      case 115:
  720.        // Search - without dialog (next match)
  721.        Search(handle, FALSE);
  722.        break;
  723.      case 116:
  724.        // Copy current poem to the clipboard
  725.        CopyToClipboard(handle, poem_buffer);
  726.        break;
  727.      case 105:
  728.        // Bigger text
  729.        ChangeHeight(1);
  730.        break;
  731.      case 106:
  732.        // Smaller text
  733.        ChangeHeight(-1);
  734.        break;
  735.      case 107:
  736.        strcpy(DesiredFont, "Roman");
  737.        DeleteFonts();
  738.        CreateFonts();
  739.        Resize();
  740.        InvalidateRect(handle, NULL, TRUE);
  741.        UpdateWindow(handle);
  742.        break;
  743.  
  744.      case 108:
  745.        strcpy(DesiredFont, "Swiss");
  746.        DeleteFonts();
  747.        CreateFonts();
  748.        Resize();
  749.        InvalidateRect(handle, NULL, TRUE);
  750.        UpdateWindow(handle);
  751.        break;
  752.  
  753.      case 109:
  754.        strcpy(DesiredFont, "Modern");
  755.        DeleteFonts();
  756.        CreateFonts();
  757.        Resize();
  758.        InvalidateRect(handle, NULL, TRUE);
  759.        UpdateWindow(handle);
  760.        break;
  761.  
  762.      case 110:
  763.        strcpy(DesiredFont, "Script");
  764.        DeleteFonts();
  765.        CreateFonts();
  766.        Resize();
  767.        InvalidateRect(handle, NULL, TRUE);
  768.        UpdateWindow(handle);
  769.        break;
  770.  
  771.      case 111:
  772.        strcpy(DesiredFont, "Decorative");
  773.        DeleteFonts();
  774.        CreateFonts();
  775.        Resize();
  776.        InvalidateRect(handle, NULL, TRUE);
  777.        UpdateWindow(handle);
  778.        break;
  779.  
  780.      case 112:
  781.        strcpy(DesiredFont, "Any");
  782.        DeleteFonts();
  783.        CreateFonts();
  784.        Resize();
  785.        InvalidateRect(handle, NULL, TRUE);
  786.        UpdateWindow(handle);
  787.        break;
  788.  
  789.      case 102:
  790.        // Compile index
  791.        Compile();
  792.        break;
  793.      case 103:
  794.        // About
  795.        lpProcAbout = MakeProcInstance((FARPROC)AboutDlgProc, Main::hInstance);
  796.        DialogBox(Main::hInstance, "DIALOG_1", handle, lpProcAbout);
  797.        FreeProcInstance(lpProcAbout);
  798.        break;
  799.      case 104:
  800.        // Exit
  801.        SendMessage(handle, WM_CLOSE, 0, 0L);
  802.        break;
  803.      default:
  804.        return FALSE;
  805.   }
  806.   return TRUE;
  807. }
  808.  
  809. // Next page/poem
  810. void MainWindow::NextPage(HWND handle)
  811. {
  812.   if (paging)
  813.     current_page ++;
  814.   else
  815.   {
  816.     current_page = 0;
  817.     GetIndexLoadPoem();
  818.   }
  819.   Resize();
  820.   InvalidateRect(handle, NULL, TRUE);
  821.   UpdateWindow(handle);
  822. }
  823.  
  824. // Previous page
  825. void MainWindow::PreviousPage(HWND handle)
  826. {
  827.   if (current_page > 0)
  828.   {
  829.     current_page --;
  830.     Resize();
  831.     InvalidateRect(handle, NULL, TRUE);
  832.     UpdateWindow(handle);
  833.   }
  834. }
  835.  
  836. // Search for a string
  837. void MainWindow::Search(HWND handle, BOOL ask)
  838. {
  839.   FARPROC lpProcSearch;
  840.   long position;
  841.  
  842.   if (ask || (search_string[0] == 0))
  843.   {
  844.     lpProcSearch = MakeProcInstance((FARPROC)SearchDlgProc, Main::hInstance);
  845.     DialogBox(Main::hInstance, "DIALOG_2", handle, lpProcSearch);
  846.     FreeProcInstance(lpProcSearch);
  847.   }
  848.   else
  849.   {
  850.     same_search = TRUE;
  851.     search_ok = TRUE;
  852.   }
  853.  
  854.   if (search_ok)
  855.   {
  856.     position = DoSearch();
  857.     if (position > -1)
  858.     {
  859.        loaded_ok = LoadPoem(data_filename, position);
  860.        Resize();
  861.        InvalidateRect(handle, NULL, TRUE);
  862.        UpdateWindow(handle);
  863.     }
  864.     else
  865.     {
  866.       last_poem_start = 0;
  867.       PoetryNotify("Search string not found.");
  868.     }
  869.   }
  870. }
  871.  
  872. // Process non-character key presses
  873. void MainWindow::KeyDown(WORD Item)
  874. {
  875.   HWND handle = GetHandle();
  876.  
  877.   switch (Item)
  878.   {
  879.     case VK_SPACE:
  880.     case VK_NEXT:
  881.        // Another poem
  882.        NextPage(handle);
  883.        break;
  884.     case VK_ESCAPE:
  885.        SendMessage(handle, WM_CLOSE, 0, 0L);
  886.        break;
  887.     case VK_PRIOR:
  888.        PreviousPage(handle);
  889.        break;
  890.     default:
  891.        break;
  892.    }
  893.  }
  894.  
  895. // Process characters
  896. void MainWindow::Char(WORD Item)
  897. {
  898.   HWND handle = GetHandle();
  899.  
  900.   switch (Item)
  901.   {
  902.     case 'n':
  903.     case 'N':
  904.       // Next match
  905.       Search(handle, FALSE);
  906.       break;
  907.     case 's':
  908.     case 'S':
  909.       // New search
  910.       Search(handle, TRUE);
  911.       break;
  912.     default:
  913.        break;
  914.    }
  915.  }
  916.  
  917. // Copy a string to the clipboard
  918. void CopyToClipboard(HWND handle, char *s)
  919. {
  920.   int length = strlen(s);
  921.   HANDLE hGlobalMemory = GlobalAlloc(GHND, (DWORD) length + 1);
  922.   if (hGlobalMemory)
  923.   {
  924.     LPSTR lpGlobalMemory = GlobalLock(hGlobalMemory);
  925.     int i, j = 0;
  926.     for (i = 0; i < length; i ++)
  927.     {
  928.       if (s[i] == '@')
  929.       {
  930.         i++;
  931.         switch (s[i])
  932.         {
  933.           case 'P':
  934.             break;
  935.           case 'T':
  936.           case 'A':
  937.           default:
  938.             i ++;
  939.             break;
  940.         }
  941.       }
  942.       else
  943.       {
  944.         lpGlobalMemory[j] = s[i];
  945.         j ++;
  946.       }
  947.     }
  948.  
  949.     GlobalUnlock(hGlobalMemory);
  950.     OpenClipboard(handle);
  951.     EmptyClipboard();
  952.     SetClipboardData(CF_TEXT, hGlobalMemory);
  953.     CloseClipboard();
  954.   }
  955. }
  956.  
  957. // Maind window procedure
  958. long MainWindow::WndProc( WORD iMessage, WORD wParam, LONG lParam )
  959. {
  960.   RECT ClientRect;
  961.   POINT point;
  962.   switch (iMessage)
  963.   {
  964.     case WM_CREATE:
  965.         break;
  966.  
  967.     case WM_PAINT:
  968.             Paint();
  969.             break;
  970.  
  971.         // Right button - popup the menu
  972.         case WM_RBUTTONDOWN:
  973.             point = MAKEPOINT(lParam);
  974.             ClientToScreen(GetHandle(), &point);
  975.             TrackPopupMenu(PopupMenu, 0, point.x, point.y, 0, GetHandle(), NULL);
  976.             break;
  977.  
  978.         case WM_DESTROY:
  979.             WritePreferences();
  980.             DeleteFonts();
  981.             DeleteObject(GreyPen);
  982.             DeleteObject(WhitePen);
  983. //            DestroyMenu(PopupMenu);
  984.             if (bitmap)
  985.               DeleteObject(bitmap);
  986.             PostQuitMessage( 0 );
  987.             break;
  988.         case WM_SYSCOMMAND:
  989.             // For some reason this doesn't work, i.e.
  990.             // the window is shown before this can take effect
  991.             if(wParam == SC_RESTORE)
  992.               ShowWindow(hWnd, SW_HIDE);
  993.  
  994.             if (!Command(wParam))
  995.             {
  996.               DefWindowProc( hWnd, iMessage, wParam, lParam );
  997.               // When we restore, get another poem
  998.               if(wParam == SC_RESTORE)
  999.               {
  1000.                 NextPage(hWnd);
  1001.                 ShowWindow(hWnd, SW_SHOW);
  1002.                 SetFocus(hWnd);
  1003.               }
  1004.             }
  1005.             break;
  1006.         case WM_COMMAND:
  1007.             Command(wParam);
  1008.             break;
  1009.         case WM_KEYDOWN:
  1010.             KeyDown(wParam);
  1011.             break;
  1012.         case WM_CHAR:
  1013.             Char(wParam);
  1014.             break;
  1015.         default:
  1016.             return DefWindowProc( hWnd, iMessage, wParam, lParam );
  1017.     }
  1018. }
  1019.  
  1020. // If data pointers are near pointers
  1021. #if defined(__SMALL__) || defined(__MEDIUM__)
  1022. inline Window *GetPointer( HWND hWnd )
  1023. {
  1024.     return (Window *) GetWindowWord( hWnd, 0 );
  1025. }
  1026. inline void SetPointer( HWND hWnd, Window *pWindow )
  1027. {
  1028.     SetWindowWord( hWnd, 0, (WORD) pWindow );
  1029. }
  1030.  
  1031. // else pointers are far
  1032. #elif defined(__LARGE__) || defined(__COMPACT__)
  1033. inline Window *GetPointer( HWND hWnd )
  1034. {
  1035.     return (Window *) GetWindowLong( hWnd, 0 );
  1036. }
  1037. inline void SetPointer( HWND hWnd, Window *pWindow )
  1038. {
  1039.     SetWindowLong( hWnd, 0, (LONG) pWindow );
  1040. }
  1041.  
  1042. #else
  1043.     #error Choose another memory model!
  1044. #endif
  1045.  
  1046. // Main window procedure - C function calls C++ version
  1047. long FAR PASCAL _export WndProc( HWND hWnd, WORD iMessage, WORD wParam,
  1048.                                  LONG lParam )
  1049. {
  1050.     // Pointer to the (C++ object that is the) window.
  1051.     Window *pWindow = GetPointer( hWnd );
  1052.  
  1053.     // The pointer pWindow will have an invalid value if the WM_CREATE
  1054.     // message has not yet been processed (we respond to the WM_CREATE
  1055.     // message by setting the extra bytes to be a pointer to the
  1056.     // (C++) object corresponding to the Window identified
  1057.     // by hWnd).  The messages that
  1058.     // precede WM_CREATE must be processed without using pWindow so we
  1059.     // pass them to DefWindowProc.
  1060.     // How do we know in general if the pointer pWindow is invalid?
  1061.     // Simple: Windows allocates the window extra bytes using LocalAlloc
  1062.     // which zero initializes memory; thus, pWindow will have a value of
  1063.     // zero before we set the window extra bytes to the 'this' pointer.
  1064.     // Caveat emptor: the fact that LocalAlloc will zero initialize the
  1065.     // window extra bytes is not documented; therefore, it could change
  1066.     // in the future.
  1067.  
  1068.     if ( pWindow == 0 )
  1069.     {
  1070.         if ( iMessage == WM_CREATE )
  1071.         {
  1072.             LPCREATESTRUCT lpcs;
  1073.  
  1074.             lpcs = (LPCREATESTRUCT) lParam;
  1075.             pWindow = (Window *) lpcs->lpCreateParams;
  1076.  
  1077.             // Store a pointer to this object in the window's extra bytes;
  1078.             // this will enable to access this object (and its member
  1079.             // functions) in WndProc where we are
  1080.             // given only a handle to identify the window.
  1081.             SetPointer( hWnd, pWindow );
  1082.             // Now let the object perform whatever
  1083.             // initialization it needs for WM_CREATE in its own
  1084.             // WndProc.
  1085.             return pWindow->WndProc( iMessage, wParam, lParam );
  1086.         }
  1087.         else
  1088.             return DefWindowProc( hWnd, iMessage, wParam, lParam );
  1089.     }
  1090.     else
  1091.         return pWindow->WndProc( iMessage, wParam, lParam );
  1092. }
  1093.  
  1094. // Turn off warning: Parameter 'lpszCmdLine' is never used in function WinMain(unsigned int,unsigned int,char far*,int)
  1095. #pragma argsused
  1096.  
  1097. // Turn off warning: 'MainWnd' is assigned a value that is never used in function WinMain(unsigned int,unsigned int,char far*,int)
  1098. #pragma option -w-aus
  1099.  
  1100. // Main windows entry point
  1101. int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine,
  1102.                     int nCmdShow )
  1103. {
  1104.     // Initialize the debugging log - development only
  1105. //    LogInitialize();
  1106.     ReadPreferences();
  1107.  
  1108.     Main::hInstance = hInstance;
  1109.     Main::hPrevInstance = hPrevInstance;
  1110.     Main::nCmdShow = nCmdShow;
  1111.  
  1112.     // A Windows class should be registered with Windows before any windows
  1113.     // of that type are created.
  1114.     // Register here all Windows classes that will be used in the program.
  1115.     // Windows classes should not be registered if an instance of
  1116.     // the program is already running.
  1117.     if ( ! Main::hPrevInstance ) {
  1118.         MainWindow::Register();
  1119.     }
  1120.  
  1121.     randomize();
  1122.     pages[0] = 0;
  1123.     search_string[0] = 0;
  1124.  
  1125.     MainWindow MainWnd;
  1126.  
  1127.     PopupMenu = LoadMenu(Main::hInstance, "MENU_1");
  1128.     HMENU SystemMenu = GetSystemMenu(MainWnd.GetHandle(), FALSE);
  1129.     DeleteMenu(SystemMenu, SC_SIZE, MF_BYCOMMAND);
  1130.     DeleteMenu(SystemMenu, SC_MAXIMIZE, MF_BYCOMMAND);
  1131.     AppendMenu(SystemMenu, MF_SEPARATOR, 0, NULL);
  1132.     AppendMenu(SystemMenu, MF_POPUP | MF_STRING, PopupMenu, "WinPoem Options");
  1133.  
  1134.     InitPoetry();
  1135.  
  1136.     CreateFonts();
  1137.     Corner1 = LoadIcon(hInstance, "ICON_1");
  1138.     Corner2 = LoadIcon(hInstance, "ICON_2");
  1139.     Corner3 = LoadIcon(hInstance, "ICON_3");
  1140.     Corner4 = LoadIcon(hInstance, "ICON_4");
  1141.  
  1142.     BlackPen = GetStockObject(BLACK_PEN);
  1143.     GreyPen = CreatePen(PS_SOLID, THICK_LINE_WIDTH, RGB(100, 100, 100));
  1144.     WhitePen = CreatePen(PS_SOLID, THICK_LINE_WIDTH, RGB(255, 255, 255));
  1145.  
  1146.     GreyBrush = GetStockObject(LTGRAY_BRUSH);
  1147.     DarkGreyBrush = GetStockObject(GRAY_BRUSH);
  1148.  
  1149.     if (nCmdShow == SW_SHOWNORMAL)
  1150.     {
  1151.       MainWnd.GetIndexLoadPoem();
  1152.       MainWnd.Resize();
  1153.     }
  1154.  
  1155.     return Main::MessageLoop();
  1156. }
  1157.  
  1158.  
  1159. // Load index file
  1160. int LoadIndex(char *file_name)
  1161. {
  1162.     long data;
  1163.     FILE *index_file;
  1164.  
  1165.     int i = 0;
  1166.     char buf[100];
  1167.  
  1168.     if (file_name)
  1169.       sprintf(buf, "%s.idx", file_name);
  1170.     if (! (file_name && (index_file = fopen(buf, "r"))))
  1171.       return 0;
  1172.     else
  1173.     {
  1174.       fscanf(index_file, "%ld", &nitems);
  1175.  
  1176.       for (i = 0; i < nitems; i++)
  1177.       {
  1178.         fscanf(index_file, "%ld", &data);
  1179.         index[i] = data;
  1180.       }
  1181.       fclose(index_file);
  1182.  
  1183.       return 1;
  1184.     }
  1185. }
  1186.  
  1187. // Get index
  1188. int GetIndex()
  1189. {
  1190.     int indexn = 0;
  1191.     double a, b;
  1192.  
  1193.     indexn = rand() % nitems;
  1194.  
  1195.     if ((indexn < 0) || (indexn > nitems))
  1196.     { PoetryError("No such poem!");
  1197.       return -1;
  1198.     }
  1199.     else
  1200.       return indexn;
  1201. }
  1202.  
  1203. // Read preferences
  1204. void ReadPreferences()
  1205. {
  1206.   GetProfileString("WinPoem", "Font", DEFAULT_FONT, DesiredFont, 64);
  1207.   desired_char_height = GetProfileInt("WinPoem", "TextSize", DEFAULT_CHAR_HEIGHT);
  1208.   XPos = GetProfileInt("WinPoem", "X", DEFAULT_X_POS);
  1209.   YPos = GetProfileInt("WinPoem", "Y", DEFAULT_Y_POS);
  1210. }
  1211.  
  1212. // Write preferences to disk
  1213. void WritePreferences()
  1214. {
  1215.   char ProfileString[50];
  1216.   RECT Rect;
  1217.  
  1218.   sprintf(ProfileString, "%d", desired_char_height);
  1219.   WriteProfileString("WinPoem", "TextSize", ProfileString);
  1220.   WriteProfileString("WinPoem", "Font", DesiredFont);
  1221.  
  1222.   GetWindowRect(GlobalWindow, &Rect);
  1223.   sprintf(ProfileString, "%d", Rect.left);
  1224.   WriteProfileString("WinPoem", "X", ProfileString);
  1225.   sprintf(ProfileString, "%d", Rect.top);
  1226.   WriteProfileString("WinPoem", "Y", ProfileString);
  1227. }
  1228.  
  1229. // Load a poem from given file, at given point in file.
  1230. // If position is > -1, use this for the position in the
  1231. // file, otherwise use index[index_ptr] to find the correct position.
  1232. BOOL LoadPoem(char *file_name, long position)
  1233. {
  1234.     char ch = 0;
  1235.     int i = 0;
  1236.     int j = 0;
  1237.     int indexn = 0;
  1238.     char buf[100];
  1239.     long data;
  1240.     FILE *data_file;
  1241.  
  1242.     paging = FALSE;
  1243.     current_page = 0;
  1244.  
  1245.     if (file_name)
  1246.       sprintf(buf, "%s.dat", file_name);
  1247.  
  1248.     if (! (file_name && (data_file = fopen(buf, "r"))))
  1249.     {
  1250.       sprintf(error_buf, "Data file %s not found.", buf);
  1251.       PoetryError(error_buf);
  1252.       return FALSE;
  1253.     }
  1254.     else
  1255.     {
  1256.       if (position > -1)
  1257.         data = position;
  1258.       else
  1259.         data = index[index_ptr];
  1260.  
  1261.       fseek(data_file, data, SEEK_SET);
  1262.  
  1263.       ch = 0;
  1264.       i = 0;
  1265.       while ((ch != EOF) && (ch != '#'))
  1266.       {
  1267.         ch = getc(data_file);
  1268.         // Add a linefeed so it will copy to the clipboard ok
  1269.         if (ch == 10)
  1270.         {
  1271.           poem_buffer[i] = 13;
  1272.           i++;
  1273.         }
  1274.  
  1275.         poem_buffer[i] = ch;
  1276.         i ++;
  1277.  
  1278.         if (i == buf_size)
  1279.         {
  1280.            sprintf(error_buf, "%s", "Poetry buffer exceeded.");
  1281.            PoetryError(error_buf);
  1282.            return FALSE;
  1283.         }
  1284.       }
  1285.       fclose(data_file);
  1286.       poem_buffer[i-1] = 0;
  1287.       return TRUE;
  1288.   }
  1289. }
  1290.  
  1291. // Do the search
  1292. long MainWindow::DoSearch()
  1293. {
  1294.     FILE *file;
  1295.     long i = 0;
  1296.     char ch = 0;
  1297.     char buf[100];
  1298.     long find_start;
  1299.     long previous_poem_start;
  1300.  
  1301.     BOOL found = FALSE;
  1302.     int search_length = strlen(search_string);
  1303.  
  1304.     if (same_search)
  1305.     {
  1306.       find_start = last_find + 1;
  1307.       previous_poem_start = last_poem_start;
  1308.     }
  1309.     else
  1310.     {
  1311.       find_start = 0;
  1312.       last_poem_start = 0;
  1313.       previous_poem_start = -1;
  1314.     }
  1315.  
  1316.     if (data_filename)
  1317.       sprintf(buf, "%s.dat", data_filename);
  1318.  
  1319.     if (! (data_filename && (file = fopen(buf, "r"))))
  1320.     {
  1321.       sprintf(error_buf, "Poetry data file %s not found\n", buf);
  1322.       PoetryError(error_buf);
  1323.       return FALSE;
  1324.     }
  1325.  
  1326.     fseek(file, find_start, SEEK_SET);
  1327.  
  1328.     while ((ch != EOF) && !found)
  1329.     {
  1330.         ch = getc(file);
  1331.         ch |= 0x0020;   // Make lower case
  1332.  
  1333.         // Only match if we're looking at a different poem
  1334.         // (no point in displaying the same poem again)
  1335.         if ((ch == search_string[i]) && (last_poem_start != previous_poem_start))
  1336.         {
  1337.           if (i == 0)
  1338.             last_find = ftell(file);
  1339.           if (i == search_length-1)
  1340.             found = TRUE;
  1341.           i ++;
  1342.         }
  1343.         else
  1344.           i = 0;
  1345.  
  1346.         if (ch == '#')
  1347.         {
  1348.             ch = getc(file);
  1349.               last_poem_start = ftell(file);
  1350.         }
  1351.     }
  1352.     fclose(file);
  1353.     if (ch == EOF)
  1354.       last_find = -1;
  1355.  
  1356.     if (found)
  1357.     {
  1358.       return last_poem_start;
  1359.     }
  1360.     else
  1361.       return -1;
  1362. }
  1363.  
  1364. // Set up poetry filenames, preferences, load the index
  1365. void InitPoetry()
  1366. {
  1367.   strcpy(index_filename, DEFAULT_POETRY_IND);
  1368.   strcpy(data_filename, DEFAULT_POETRY_DAT);
  1369.  
  1370.   TryLoadIndex();
  1371. }
  1372.  
  1373. // Load index (or compile it if none found)
  1374. void TryLoadIndex()
  1375. {
  1376.   index_ok = LoadIndex(index_filename);
  1377.   if (!index_ok)
  1378.   {
  1379.       PoetryError("Index file not found; will compile new one");
  1380.       index_ok = Compile();
  1381.   }
  1382. }
  1383.  
  1384. // Error message
  1385. void PoetryError(char *Msg)
  1386. {
  1387.   MessageBox(GlobalWindow, (LPSTR)Msg, (LPSTR)"Error", MB_OK | MB_ICONEXCLAMATION);
  1388. }
  1389.  
  1390. // Notification (change icon to something appropriate!)
  1391. void PoetryNotify(char *Msg)
  1392. {
  1393.   MessageBox(GlobalWindow, (LPSTR)Msg, (LPSTR)"Notification", MB_OK |
  1394.                                                         MB_ICONINFORMATION);
  1395. }
  1396.  
  1397. // Initialize a log file (for debugging purposes)
  1398. void LogInitialize()
  1399. {
  1400.   char *logfile_name = "c:\\log";
  1401.   OFSTRUCT OfStruct;         // Information from OpenFile()
  1402.   int WinFile;
  1403.  
  1404.   WinFile = OpenFile(logfile_name, &OfStruct, OF_CREATE);
  1405.   _lclose(WinFile);
  1406. }
  1407.  
  1408. // Write a message to the log file
  1409. void LogMessage(char *Msg)
  1410. {
  1411.   char *logfile_name = "c:\\log";
  1412.   FILE *logfile;
  1413.   OFSTRUCT OfStruct;         // Information from OpenFile()
  1414.   int WinFile;
  1415.  
  1416.   if ((WinFile = OpenFile(logfile_name, &OfStruct, OF_READWRITE)) != -1)
  1417.   {
  1418.    logfile = fdopen(WinFile, "a");
  1419.    fprintf(logfile, "%s\n", Msg);
  1420.    fclose(logfile);
  1421.   }
  1422. }
  1423.  
  1424. // 'About' dialog box
  1425. BOOL FAR PASCAL AboutDlgProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam)
  1426. {
  1427.   switch (message)
  1428.   {
  1429.     case WM_INITDIALOG:
  1430.       return TRUE;
  1431.     case WM_COMMAND:
  1432.       switch (wParam)
  1433.       {
  1434.         case 101:
  1435.           EndDialog(hDlg, 0);
  1436.           return TRUE;
  1437.       }
  1438.       break;
  1439.   }
  1440.   return FALSE;
  1441. }
  1442.  
  1443. // Build up and save an index into the poetry data file, for
  1444. // rapid random access
  1445. BOOL Compile()
  1446. {
  1447.     FILE *file;
  1448.     long i = 0;
  1449.     int j;
  1450.     char ch = 0;
  1451.     char buf[100];
  1452.  
  1453.     if (data_filename)
  1454.       sprintf(buf, "%s.dat", data_filename);
  1455.  
  1456.     if (! (data_filename && (file = fopen(buf, "r"))))
  1457.     {
  1458.       sprintf(error_buf, "Poetry data file %s not found\n", buf);
  1459.       PoetryError(error_buf);
  1460.       return FALSE;
  1461.     }
  1462.  
  1463.     nitems = 0;
  1464.  
  1465.     // Do first one (?)
  1466.     index[nitems] = 0;
  1467.     nitems ++;
  1468.  
  1469.     // Do rest
  1470.     while (ch != EOF)
  1471.     {
  1472.         ch = getc(file);
  1473.         i ++;
  1474.         if (ch == '#')
  1475.         {
  1476.             ch = getc(file);
  1477.             long data;
  1478.             data = ftell(file);
  1479.             index[nitems] = data;
  1480.             nitems ++;
  1481.         }
  1482.     }
  1483.     fclose(file);
  1484.  
  1485.     if (index_filename)
  1486.       sprintf(buf, "%s.idx", index_filename);
  1487.     if (! (data_filename && (file = fopen(buf, "w"))))
  1488.     {
  1489.       sprintf(error_buf, "Poetry index file %s cannot be created\n", buf);
  1490.       PoetryError(error_buf);
  1491.       return FALSE;
  1492.     }
  1493.  
  1494.     fprintf(file, "%ld\n\n", nitems);
  1495.     for (j = 0; j < nitems; j++)
  1496.       fprintf(file, "%ld\n", index[j]);
  1497.  
  1498.     fclose(file);
  1499.     PoetryNotify("Poetry index compiled.");
  1500.     return TRUE;
  1501. }
  1502.  
  1503. // Dialog proc for entering search string
  1504. BOOL FAR PASCAL SearchDlgProc(HWND hDlg, unsigned message, WORD wParam, LONG lParam)
  1505. {
  1506.   char Text[120];
  1507.  
  1508.   switch (message)
  1509.   {
  1510.     case WM_INITDIALOG:
  1511.       if (search_string[0] != 0)
  1512.         SetDlgItemText(hDlg, 101, search_string);
  1513.       return TRUE;
  1514.     case WM_COMMAND:
  1515.       switch (wParam)
  1516.       {
  1517.         case 101:
  1518.         // Edit
  1519.         return TRUE;
  1520.  
  1521.         // Ok
  1522.         case 102:
  1523.           // Find string
  1524.           GetDlgItemText(hDlg, 101, Text, 40);
  1525.           if (strcmp(Text, search_string) == 0)
  1526.             same_search = TRUE;
  1527.           else
  1528.             same_search = FALSE;
  1529.           strcpy(search_string, Text);
  1530.           search_ok = TRUE;
  1531.           EndDialog(hDlg, 0);
  1532.           return TRUE;
  1533.  
  1534.         // Cancel
  1535.         case 103:
  1536.           EndDialog(hDlg, 0);
  1537.           search_ok = FALSE;
  1538.           return TRUE;
  1539.       }
  1540.       break;
  1541.  
  1542.   }
  1543.   return FALSE;
  1544. }
  1545.  
  1546.